#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define MAXSIZE         1024
#define OPEN_MAX        2
#define LISTENQ         20
#define POLL_TOUT       1000
#define SERV_PORT       1234

struct cli_buf_st {
    int fd;
    char *buffer;
    int size;
};

void setnonblocking(int sock)
{
    int opts;

    opts = fcntl(sock, F_GETFL);
    if (opts < 0) {
        perror("fcntl(sock,GETFL)");
        return;
    }

    opts = opts|O_NONBLOCK;
    if (fcntl(sock, F_SETFL, opts) < 0) {
        perror("fcntl(sock,SETFL,opts)");
        return;
    }
}

int cli_buf_init(struct cli_buf_st *cli, int n)
{
    int i;

    for (i = 0; i < n; i++) {
        cli[i].fd = -1;
        cli[i].buffer = NULL;
        cli[i].size = 0;
    }

    return 0;
}

struct cli_buf_st *cli_buf_find_empty(struct cli_buf_st *root, int n)
{
    int i;

    for (i = 0; i < n; i++) {
        if (root[i].fd == -1) {
            return &root[i];
        }
    }

    return NULL; 
}

struct cli_buf_st *cli_buf_find(struct cli_buf_st *root, int n, int fd)
{
    int i;

    for (i = 0; i < n; i++) {
        if (root[i].fd == fd) {
            return &root[i];
        }
    }

    return NULL; 
}

int cli_buf_new(struct cli_buf_st *cli, int fd)
{
    printf("new cli fd %d\n", fd);

    cli->fd = fd;
    cli->buffer = malloc(MAXSIZE);
    if (cli->buffer == NULL) {
        fprintf(stderr, "malloc error");
        return -1;
    }
    cli->size = 0;

    return 0;
}

int cli_buf_del(struct cli_buf_st *cli)
{
    printf("del cli fd %d\n", cli->fd);

    cli->fd = -1;
    if (cli->buffer) {
        free(cli->buffer);
        cli->buffer = NULL;
    }
    cli->size = 0;

    return 0;
}

int main(int argc, char *argv[])
{
    int listenfd, sockfd, epfd, nfds;
    int i, port ,n;
    socklen_t clilen;
    struct epoll_event ev, events[OPEN_MAX+1];
    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    struct cli_buf_st cli_buf[OPEN_MAX];
    struct cli_buf_st *cli;

    port = SERV_PORT;
    if (argc == 2) {
        if ((port = atoi(argv[1])) < 0) {
            fprintf(stderr, "Usage: %s <port>\n", argv[0]);
            exit(1);
        }
    }

    // prepare the client buffer
    cli_buf_init(cli_buf, OPEN_MAX);

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    setnonblocking(listenfd);

    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(port);

    bind(listenfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    listen(listenfd, LISTENQ);

    epfd = epoll_create(256);
    ev.data.fd = listenfd;
    ev.events = EPOLLIN | EPOLLET;

    // register epoll event
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    for (;;) {
        //wait the epoll event
        nfds = epoll_wait(epfd, events, OPEN_MAX+1, POLL_TOUT);

        if (nfds == 0) {
            printf("timeout.\n");
            continue;
        }

        else if (nfds < 0) {
            fprintf(stderr, "epoll_wait error: %s", strerror(errno));
            exit(1);
        }

        //process the all event
        for (i = 0; i < nfds; ++i)
        {
            if (events[i].data.fd == listenfd)      //如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口，建立新的连接。
            {
                clilen = sizeof(clientaddr);
                sockfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clilen);
                if (sockfd < 0) {
                    perror("sockfd < 0");
                    exit(1);
                }

                setnonblocking(sockfd);
                printf("accapt a connection from %s:%d\n",
                        inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

                cli = cli_buf_find_empty(cli_buf, OPEN_MAX);
                if (cli) {
                    cli_buf_new(cli, sockfd);
                } else {
                    fprintf(stderr, "cli is FULL\n");
                    close(sockfd);
                    continue;
                }
                
                ev.data.fd = sockfd;
                ev.events = EPOLLIN|EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
            }

            else if(events[i].events & EPOLLIN)     //如果是已经连接的用户，并且收到数据，那么进行读入。
            {
                sockfd = events[i].data.fd;
                printf("[%d]EPOLLIN\n", sockfd);

                cli = cli_buf_find(cli_buf, OPEN_MAX, sockfd);
                if (cli == NULL) {
                    fprintf(stderr, "oops: read fd %d not found client\n", sockfd);
                    exit(2);
                }

                if ((n = read(sockfd, cli->buffer, MAXSIZE - 1)) <= 0) {
                    if ((errno == ECONNRESET) || (n == 0)) {
                        fprintf(stderr, "client %d reset/closed\n", sockfd);
                        events[i].data.fd = -1;
                    }
                    else {
                        printf("read %d error %d: %s\n", sockfd, errno, strerror(errno));
                    }

                    close(sockfd);
                    cli_buf_del(cli);
                    ev.data.fd = sockfd;
                    ev.events = EPOLLIN | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev);
                    continue;
                }

                cli->buffer[n] = '\0';
                cli->size = n;
                //printf("read %d [%s]\n", cli->size, cli->buffer);

                ev.data.fd = sockfd;
                ev.events = EPOLLOUT | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }

            else if (events[i].events & EPOLLOUT)   // 如果有数据发送
            {
                sockfd = events[i].data.fd;
                printf("[%d]EPOLLOUT\n", sockfd);

                cli = cli_buf_find(cli_buf, OPEN_MAX, sockfd);
                if (cli == NULL) {
                    fprintf(stderr, "oops: write fd %d not found client\n", sockfd);
                    exit(2);
                }

                if ((n = write(sockfd, cli->buffer, cli->size)) <= 0) {
                    if ((errno = ECONNRESET) || n == 0) {
                        events[i].data.fd = -1;
                    }
                    else {
                        printf("write error!, %d: %s\n", n, strerror(errno));
                    }

                    close(sockfd);
                    cli_buf_del(cli);
                    ev.data.fd = sockfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev);
                    continue;
                }

                //printf("write %d [%s]\n", cli->size, cli->buffer);

                ev.data.fd = sockfd;
                ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }
        }
    }

    exit(0);
}
